package edu.eece261.fall11;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import com.google.gson.*;

import edu.eece261.fall11.Messages.ActionType;
import edu.eece261.fall11.Messages.MessageToClient;
import edu.eece261.fall11.Messages.MessageToServer;
import edu.eece261.fall11.Messages.ResponseType;
import edu.eece261.fall11.Messages.ShopList;

import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
import android.util.Log;

public class HTTPHelp {

	// Let's be a Singleton
	private static HTTPHelp instanceActivity = null;
	private static HTTPHelp instanceService = null;
	private final String URL = "http://shopping-list-android.appspot.com/android";
	// "http://10.0.2.2:8888/android";

	public static final String PREFS_NAME = "SHOPPINGLISTLOGIN";

	private String username;
	private String password;
	private boolean loggedIn = false;
	private String UUID;
	Gson gson;
	DefaultHttpClient httpClient;

	public static HTTPHelp getActivityHTTPHelp() {
		if (instanceActivity == null) {
			instanceActivity = new HTTPHelp();
			return instanceActivity;
		} else {
			return instanceActivity;
		}
	}

	public static HTTPHelp getServiceHTTPHelp() {
		if (instanceService == null) {
			instanceService = new HTTPHelp();
			return instanceService;
		} else {
			return instanceService;
		}
	}

	private HTTPHelp() {
		gson = new Gson();
		httpClient = new DefaultHttpClient();
	}

	public boolean createUser(String username, String password, String email,
			String name) {
		MessageToClient received = notLoggedInPost(new Messages.MessageToServer()
				.setAction(ActionType.CREATEUSER).setUsername(username)
				.setPassword(password).setEmail(email).setName(name));
		if (received == null) {
			return false;
		} else if (received.getResponse() == ResponseType.SUCCESS) {
			return true;
		} else {
			return false;
		}
	}

	public boolean logIn(String username, String password) {
		MessageToClient received = notLoggedInPost(new Messages.MessageToServer()
				.setResponse(ResponseType.SUCCESS).setUsername(username)
				.setPassword(password).setAction(ActionType.LOGIN));
		if (received == null) {
			loggedIn = false;
			return false;
		} else if (received.getResponse() == ResponseType.SUCCESS) {
			loggedIn = true;
			this.username = username;
			this.password = password;
			this.UUID = received.getUuid();
			return true;
		} else {
			loggedIn = false;
			return false;
		}
	}

	public boolean pollServer(Context context) {
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.POLL).setUsername(username).setLastModifiedTime(
				settings.getLong(username + "lastmodified", 0));
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient != null
				&& messageToClient.getResponse() == ResponseType.SUCCESS) {
			SharedPreferences.Editor editor = settings.edit();
			editor.putLong(username + "lastmodified", messageToClient
					.getLastModified());
			editor.commit();

			LinkedList<Messages.ShopList> changedLists = messageToClient
					.getLists();
			for (int i = 0; i < changedLists.size(); ++i) {
				ListDBAdapter listDBA = ListDBAdapter
						.getListDBAdapter(context);
				listDBA.open();
				Cursor localResults = listDBA
						.getEntryMatchingTitle(changedLists.get(i).getTitle());
				if (localResults.getCount() > 0) {
					localResults.moveToFirst();
					ShopList currentList = changedLists.get(i);
					if (currentList.isFinished() == -1) {
						leaveList(currentList.getTitle(), context);
					} else {
						listDBA.updateEntry(localResults.getInt(0),
								currentList.getTitle(), gson
										.toJson(currentList.getItems()),
								gson.toJson(currentList.getUpdaters()), gson
										.toJson(currentList.getTimestamps()),
								currentList.getOwner(), currentList
										.getLastUpdater(), currentList
										.isFinished(), currentList
										.getGlobal_id());
					}
				} else {
					ShopList currentList = changedLists.get(i);
					listDBA.insertList(currentList.getTitle(), gson
							.toJson(currentList.getItems()), gson
							.toJson(currentList.getUpdaters()), gson
							.toJson(currentList.getTimestamps()), currentList
							.getOwner(), currentList.getLastUpdater(),
							currentList.isFinished(), currentList
									.getGlobal_id());
				}
			}
			return true;
		}
		return false;
	}

	public boolean addList(String title, String listPassword, Context context) {
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		String user = settings.getString("user", "");

		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.ADDLIST).setUsername(user).setListPassword(listPassword).setTitle(title);
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient.getResponse() == ResponseType.SUCCESS) {
			ListDBAdapter listDBA = ListDBAdapter.getListDBAdapter(context);
			ShopList currentList = messageToClient.getLists().get(0);
			listDBA.insertList(currentList.getTitle(), gson
					.toJson(currentList.getItems()), gson
					.toJson(currentList.getUpdaters()), gson
					.toJson(currentList.getTimestamps()), currentList
					.getOwner(), currentList.getLastUpdater(), currentList
					.isFinished(), currentList.getGlobal_id());
			return true;
		} else {
			return false;
		}
	}

	public LinkedList<ShopList> findLists(int offset, Context context) {
		LinkedList<ShopList> results = new LinkedList<ShopList>();
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		String user = settings.getString("user", "");
		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.FINDLISTS).setUsername(user);
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient.getResponse() == ResponseType.SUCCESS) {
			results = messageToClient.getLists();
		}
		return results;
	}

	public boolean joinList(String title, Context context) {
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		String user = settings.getString("user", "");
		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.JOINLIST).setUsername(user).setTitle(title);
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient.getResponse() == ResponseType.SUCCESS) {
			ShopList currentList = messageToClient.getLists().get(0);
			ListDBAdapter.getListDBAdapter(context).insertList(
					currentList.getTitle(),
					gson.toJson(currentList.getItems()),
					gson.toJson(currentList.getUpdaters()),
					gson.toJson(currentList.getTimestamps()),
					currentList.getOwner(), currentList.getLastUpdater(),
					currentList.isFinished(), currentList.getGlobal_id());
			return true;
		} else {
			return false;
		}
	}

	public boolean addItem(String title, String item, Context context) {
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		String user = settings.getString("user", "");
		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.ADDITEM).setUsername(user).setTitle(title)
				.setItem(item);
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient.getResponse() == ResponseType.SUCCESS) {
			ShopList currentList = messageToClient.getLists().get(0);
			ListDBAdapter.getListDBAdapter(context).updateEntryFromGlobal(
					currentList.getTitle(),
					gson.toJson(currentList.getItems()),
					gson.toJson(currentList.getUpdaters()),
					gson.toJson(currentList.getTimestamps()),
					currentList.getOwner(), currentList.getLastUpdater(),
					currentList.isFinished(), currentList.getGlobal_id());
			return true;
		} else {
			return false;
		}
	}
	
	public boolean editItem(String title, String item, long itemTimestamp, Context context) {
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		String user = settings.getString("user", "");
		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.EDITITEM).setUsername(user).setTitle(title)
				.setItem(item).setItemTimestamp(itemTimestamp);
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient.getResponse() == ResponseType.SUCCESS) {
			ShopList currentList = messageToClient.getLists().get(0);
			ListDBAdapter.getListDBAdapter(context).updateEntryFromGlobal(
					currentList.getTitle(),
					gson.toJson(currentList.getItems()),
					gson.toJson(currentList.getUpdaters()),
					gson.toJson(currentList.getTimestamps()),
					currentList.getOwner(), currentList.getLastUpdater(),
					currentList.isFinished(), currentList.getGlobal_id());
			return true;
		} else {
			return false;
		}
	}

	public boolean deleteItem(String title, long itemTimestamp, Context context) {
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		String user = settings.getString("user", "");
		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.DELETEITEM).setUsername(user).setTitle(title)
				.setItemTimestamp(itemTimestamp);
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient.getResponse() == ResponseType.SUCCESS) {
			ShopList currentList = messageToClient.getLists().get(0);
			ListDBAdapter.getListDBAdapter(context).updateEntryFromGlobal(
					currentList.getTitle(),
					gson.toJson(currentList.getItems()),
					gson.toJson(currentList.getUpdaters()),
					gson.toJson(currentList.getTimestamps()),
					currentList.getOwner(), currentList.getLastUpdater(),
					currentList.isFinished(), currentList.getGlobal_id());
			return true;
		} else {
			return false;
		}
	}
	
	public boolean leaveList(String title, Context context) {
		SharedPreferences settings = context
				.getSharedPreferences(PREFS_NAME, 0);
		String user = settings.getString("user", "");
		MessageToServer sendMe = new MessageToServer().setAction(
				ActionType.LEAVELIST).setUsername(user).setTitle(title);
		MessageToClient messageToClient = loggedInPost(sendMe, false);

		if (messageToClient.getResponse() == ResponseType.SUCCESS) {
			ShopList currentList = messageToClient.getLists().get(0);
			ListDBAdapter.getListDBAdapter(context).removeEntryFromGlobal(
					currentList.getGlobal_id());
			return true;
		} else {
			return false;
		}
	}

	public MessageToClient notLoggedInPost(MessageToServer sendMe) {
		DefaultHttpClient httpClient = new DefaultHttpClient();

		HttpPost postMethod = new HttpPost(URL);

		List<NameValuePair> postData = new ArrayList<NameValuePair>();
		postData.add(new BasicNameValuePair("JSONMessageToServer", gson
				.toJson(sendMe)));

		Messages.MessageToClient mtcResponse = null;

		try {
			postMethod.setEntity(new UrlEncodedFormEntity(postData));

			HttpResponse response = httpClient.execute(postMethod);

			Log.e("HTTP REQUEST", gson.toJson(sendMe));

			String JSONMessageToClient = convertStreamToString(response
					.getEntity().getContent());
			Log.e("HTTP RESULTS", JSONMessageToClient);
			mtcResponse = gson.fromJson(JSONMessageToClient,
					Messages.MessageToClient.class);
			Log.e("HTTP RESPONSE", mtcResponse.toString());
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			Log.e("HTTP ERROR ", "Unsupported encoding.");
		} catch (HttpResponseException e) {
			e.printStackTrace();
			Log.e("HTTP ERROR CODE", "" + e.getStatusCode());
		} catch (ClientProtocolException e) {
			e.printStackTrace();
			Log.e("HTTP ERROR ", "Protocol exception.");
		} catch (IOException e) {
			e.printStackTrace();
			Log.e("HTTP ERROR ", "General IO Exception.");
		}

		return mtcResponse;

	}

	public MessageToClient loggedInPost(MessageToServer sendMe,
			boolean secondTry) {
		if (!loggedIn && username != null && password != null) {
			logIn(username, password);
		}

		if (loggedIn) {
			sendMe.setUsername(username);
			sendMe.setUuid(UUID);

			HttpPost postMethod = new HttpPost(URL);

			List<NameValuePair> postData = new ArrayList<NameValuePair>();
			postData.add(new BasicNameValuePair("JSONMessageToServer", gson
					.toJson(sendMe)));

			Messages.MessageToClient mtcResponse = null;

			try {
				postMethod.setEntity(new UrlEncodedFormEntity(postData));

				HttpResponse response = httpClient.execute(postMethod);

				String JSONMessageToClient = convertStreamToString(response
						.getEntity().getContent());
				Log.e("HTTP RESULTS", JSONMessageToClient);
				mtcResponse = gson.fromJson(JSONMessageToClient,
						Messages.MessageToClient.class);

				if ((mtcResponse.getResponse() == ResponseType.FAILURENOTLOGGEDIN)
						&& !secondTry) {
					logIn(username, password);
					return loggedInPost(sendMe, true);
				} else {
					loggedIn = false;
				}

				Log.e("HTTP RESPONSE", mtcResponse.toString());
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
				Log.e("HTTP ERROR ", "Unsupported encoding.");
			} catch (HttpResponseException e) {
				e.printStackTrace();
				Log.e("HTTP ERROR CODE", "" + e.getStatusCode());
			} catch (ClientProtocolException e) {
				e.printStackTrace();
				Log.e("HTTP ERROR ", "Protocol exception.");
			} catch (IOException e) {
				e.printStackTrace();
				Log.e("HTTP ERROR ", "General IO Exception.");
			}

			return mtcResponse;
		} else {
			return null;
		}

	}

	public static String convertStreamToString(InputStream is)
			throws IOException {
		if (is != null) {
			StringBuilder sb = new StringBuilder();
			char[] buf = new char[1];

			try {
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(is, "UTF-8"));
				while (reader.read(buf) != -1) {
					sb.append(buf);
				}
			} finally {
				is.close();
			}
			return sb.toString();
		} else {
			return "";
		}
	}

	public boolean isLoggedIn() {
		return loggedIn;
	}
}